%{
	#include "lexico.h" 
	#include "tablasimbolos.h"
	#include <stdio.h>
	
	extern int yylex();
	extern char *yytext;	
	int yyerror(char *s);
	void install_symbol(char *);
	int is_symbol(char *);	
	
	struct lista_variables *add_var(struct lista_variables *, struct registro *);
	struct lista_parametros *add_par(struct lista_parametros *, struct registro *);
    int in_lista_parm(struct lista_parametros *, struct registro *);
	int in_lista_var(struct lista_variables *, struct registro *);
	int num_parm(struct lista_parametros *);
	void errorSemantico(char *);
	int nrolinea = 1;
	int subrutina_id = 1;	
	struct lista_variables *lista_var = NULL;
	struct lista_parametros *lista_par = NULL;
	
	int static_dir = 0;
	int field_dir = 0;
	int local_dir = 0;
	int param = 0;
	
	struct types 
	{
		char *lexema;
		int tipo;
		int contexto;		
	};	
	
%}

%union {
	char *lexema;
	struct types *tipos;
}

%token <lexema> TID
%token TPARA
%token TPARC
%token TCRA
%token TCRC
%token TLLA
%token TLLC
%token TCOMA
%token TPCOMA
%token TIGUAL
%token TPUNTO
%token TMAS
%token TMENOS
%token TMULT
%token TDIV
%token TAND
%token TOR
%token TNOT
%token TMAY
%token TMEN
%token TCLASS
%token TCONST
%token TMETHOD
%token TFUNCT
%token TINT
%token TBOOL
%token TCHAR
%token TVOID
%token TVAR
%token TSTAT
%token TFLD
%token TLET
%token TDO
%token TIF
%token TELSE
%token TWHILE
%token TRETURN
%token TTRUE
%token TFALSE
%token TNULL
%token <lexema> TTHIS
%token <lexema> TCTECHAR 
%token <lexema> TCTEINT

%left TOR
%left TAND
%left TIGUAL TNOT
%left TMAY TMEN
%left TMAS TMENOS
%left TDIV TMULT
%right TMENOSU


%type <tipos> tipo_variable_clase

%type <tipos> subrutina
%type <tipos> tipo_subrutina 
%type <tipos> tipo_retorno 
%type <tipos> tipo 
//%type <tipos> declaracion_variable_subrutina
%%



/* -------------------------------------------------------------------------- */
declaracion_clase : TCLASS TID TLLA cuerpo_clase TLLC { install_symbol($2);
														add_context(get_symbol($2), 1);
													
													  }
														
				  | TCLASS TID TLLA cuerpo_clase error { yyerror("Error en la declaracion de clase, se espera: } "); }	                  
				  | error
				  
                  

/* -------------------------------------------------------------------------- */
cuerpo_clase : declaracion_variables_clase declaracion_subrutinas
			 | declaracion_subrutinas
			 | declaracion_variables_clase error { yyerror("Error en la declaracion de clase: No se han declarado subrutinas"); } 
			 | error

/* -------------------------------------------------------------------------- */
/* Declaracion de las varibles de la clase */
declaracion_variables_clase : declaracion_variable_clase
							| declaracion_variables_clase declaracion_variable_clase					
						
/* -------------------------------------------------------------------------- */
declaracion_variable_clase : tipo_variable_clase tipo lista_id {  struct lista_variables *vars, *tmp_var;
                                                                  for(vars = lista_var; vars != NULL; vars = vars->sgte)
                                                                  { 
                                                                    add_context(vars->variables, $1->contexto);
                                                                    add_tipo(vars->variables, $2->tipo);
																	  
																	if(!in_lista_var(tmp_var, vars->variables))
																	{	tmp_var =  add_var(tmp_var, vars->variables);
																		if($1->contexto == 2)
																		{
																			vars->variables->atributos->direccion = static_dir;
																			static_dir++;
																		}
																		if($1->contexto == 3)
																		{
																			vars->variables->atributos->direccion = field_dir;
																			field_dir++;
																		}                                                                
																	}
																	else
																	{
																		char *msg1 = " ya ha sido declarado ";
																		char *msg = (char *)malloc(sizeof(char)*(strlen(vars->variables->id)+strlen(msg1)+1));
																		msg = strcat(msg,vars->variables->id);
																		msg = strcat(msg, msg1);									    	                                        
																		errorSemantico(msg);
																		del_symbol(vars->variables);
																	}
																	
																  }
                                                                } TPCOMA 
/* -------------------------------------------------------------------------- */
tipo_variable_clase : TSTAT { $$ = (struct types *)malloc(sizeof(struct types));
                              $$->contexto = 2;
                            }
					| TFLD  { $$ = (struct types *)malloc(sizeof(struct types));
                              $$->contexto = 3;
                            }

/* -------------------------------------------------------------------------- */
declaracion_subrutinas : declaracion_subrutina
					   | declaracion_subrutinas declaracion_subrutina

/* -------------------------------------------------------------------------- */
declaracion_subrutina : subrutina TID { if( get_symbol($2) == NULL)
		                                {
									  		install_symbol($2);
								       		add_context(get_symbol($2), $1->contexto);
									   		add_function_id(get_symbol($2), subrutina_id);
									   		
									   	}
									   	else
									   	{
									   		char *msg1 = " ya ha sido declarada";
									   		char *msg = (char *)malloc(sizeof(char)*(strlen($2)+strlen(msg1)+1));
									   		msg = strcat(msg,$2);
									    	msg = strcat(msg, msg1);
									   		errorSemantico(msg);
									   	}
									  } TPARA parametros_formales {  
																	 struct lista_parametros *parm, *tmp_par;
																	 struct registro *tmpreg;
																	 tmp_par = NULL;																	 
																  	 for (parm = lista_par; parm != NULL; parm = parm->sgte)
																  	 {	
																  		 add_function_id(parm->parametros, get_function_id(get_symbol($2)));
																  		 add_context(parm->parametros, 8);
																  		
																  	 }																  	 
																  	 for(tmpreg = ST; tmpreg!= NULL; tmpreg = tmpreg->sgte)
																	 {
																  	 	
																  	 	if(!(strcmp(tmpreg->id,get_symbol($2)->id) == 0))
																  	 	{
																  	 		if(tmpreg->atributos->ID_funcion == get_function_id(get_symbol($2)))
																  	 		{   
                                                                                if(!in_lista_parm(tmp_par, tmpreg))
																				{                                                                                    
                                                                                    tmp_par = add_par(tmp_par, tmpreg);																	
                                                                                }
																				
                                                                                else
                                                                                {
                                                                                    char *msg1 = " ya ha sido declarado ";
									   		                                        char *msg = (char *)malloc(sizeof(char)*(strlen(tmpreg->id)+strlen(msg1)+1));
									   		                                        msg = strcat(msg,tmpreg->id);
									    	                                        msg = strcat(msg, msg1);									    	                                        
									   		                                        errorSemantico(msg);
									   		                                        del_symbol(tmpreg);
                                                                                }
																  	 		}
																  	 	}																  	 
																  	 }
																	 param = num_parm(tmp_par);
																	 //printf("%d\n", param);
																   	 lista_par = NULL; 				                              
									  } TPARC cuerpo_subrutina { subrutina_id++; } {
										  
										 
										  char *texto = $2;
										  char *texto1 = (char *)malloc(sizeof(char)*(strlen($2)+strlen(texto)+1));										  
										  texto1 = strcat(texto1, "Function ");
										  texto1 = strcat(texto1, texto);										  
										  printf("%s %d\n", texto1, param );
									  }
					 /* | subrutina TID TPARA error { yyerror("Error en la lista de parametros"); } */

/* -------------------------------------------------------------------------- */
subrutina : tipo_subrutina tipo_retorno { $$ = (struct types *)malloc(sizeof(struct types));
	                                      $$->contexto = $1->contexto;
	                                      $$->tipo = $2->tipo;	
										}
          | tipo_subrutina tipo  { $$ = (struct types *)malloc(sizeof(struct types));
	                               $$->contexto = $1->contexto;
	                               $$->tipo = $2->tipo;	
                                 }   

/* -------------------------------------------------------------------------- */
tipo_subrutina : TCONST { $$ = (struct types *)malloc(sizeof(struct types));
			   			  $$->contexto = 5;	
		       			}
               | TMETHOD { $$ = (struct types *)malloc(sizeof(struct types));
	                       $$->contexto = 6;	
                         }
               | TFUNCT { $$ = (struct types *)malloc(sizeof(struct types));
	                      $$->contexto = 7;	
                        }

/* -------------------------------------------------------------------------- */
tipo_retorno : TVOID { $$ = (struct types *)malloc(sizeof(struct types));
	                   $$->tipo = 3;
	                 }

/* -------------------------------------------------------------------------- */
parametros_formales : /* vacio */
					| lista_parametros

/* -------------------------------------------------------------------------- */
lista_parametros : tipo TID { install_symbol($2);
                              add_tipo(get_symbol($2),$1->tipo);
				 			  lista_par = add_par(lista_par, get_symbol($2));
				 			  
				 			}
				 | lista_parametros TCOMA tipo TID  { install_symbol($4);
				                                      add_tipo(get_symbol($4),$3->tipo);
				 									  lista_par = add_par(lista_par, get_symbol($4));
				 								    }
                 | lista_parametros error { yyerror("Error en la lista de parametros"); }

/* -------------------------------------------------------------------------- */
tipo : TCHAR { $$ = (struct types *)malloc(sizeof(struct types));
	           $$->tipo = 2;
	         }	
	 | TINT	 { $$ = (struct types *)malloc(sizeof(struct types));
	           $$->tipo = 1;
	         }
	 | TBOOL { $$ = (struct types *)malloc(sizeof(struct types));
	           $$->tipo = 4;
	         }
	 | TID   { $$ = (struct types *)malloc(sizeof(struct types));
	           $$->tipo = 5;
	         }

/* -------------------------------------------------------------------------- */
cuerpo_subrutina : TLLA TLLC 
				 | TLLA declaracion_variables_subrutina lista_instrucciones TLLC 
				 | TLLA lista_instrucciones TLLC 
				 | TLLA error TLLC { yyerror("Error en el cuerpo de la subrutina"); }
                 | TLLA declaracion_variables_subrutina error TLLC { yyerror("Error en el cuerpo de la subrutina"); }

/* -------------------------------------------------------------------------- */
declaracion_variables_subrutina : declaracion_variable_subrutina
								| declaracion_variables_subrutina declaracion_variable_subrutina
								| declaracion_variables_subrutina error { yyerror("Error en la declaracion de variables"); }
                                | error { yyerror("Error en la declaracion de variables"); }

/* -------------------------------------------------------------------------- */
declaracion_variable_subrutina : TVAR tipo lista_id TPCOMA { struct registro *tmpreg1;
															 struct lista_variables *tmp_var;  
                                                             for(tmpreg1 = ST;  tmpreg1 != NULL; tmpreg1 = tmpreg1->sgte)
                                                             { 
                                                                if(tmpreg1->atributos->ID_contexto == 0)
                                                                {
                                                                    add_context(tmpreg1, 4);
                                                                    add_tipo(tmpreg1, $2->tipo); 
																	add_function_id(tmpreg1, subrutina_id);
																	tmpreg1->atributos->direccion = local_dir;
																	local_dir++;
																	if(!in_lista_var(tmp_var, tmpreg1))
																		tmp_var =  add_var(tmp_var, tmpreg1);
																	else
																	{
																		char *msg1 = " ya ha sido declarado ";
																		char *msg = (char *)malloc(sizeof(char)*(strlen(tmpreg1->id)+strlen(msg1)+1));
																		msg = strcat(msg,tmpreg1->id);
																		msg = strcat(msg, msg1);									    	                                        
																		errorSemantico(msg);
																		del_symbol(tmpreg1);
																	}
														    	}															  
														   	 } 	
														   }

/* -------------------------------------------------------------------------- */
lista_id : TID { install_symbol($1);
                 lista_var = add_var(lista_var, get_symbol($1));
               }
               
		 | lista_id TCOMA TID { install_symbol($3);
                                lista_var = add_var(lista_var, get_symbol($3));		  
		                      }
		 | lista_id error TID { yyerror("Error en la declaracion de variables"); }
         | lista_id TCOMA error { yyerror("Error en la declaracion de variables"); }

/* -------------------------------------------------------------------------- */
lista_instrucciones : instruccion
                    | lista_instrucciones instruccion
                    | lista_instrucciones error { yyerror("Error en la instruccion"); }

/* -------------------------------------------------------------------------- */
instruccion : if_statement 

/* -------------------------------------------------------------------------- */
otras_instrucciones : let_statement
					| while_statemet
					| do_statement
					| return_statement
					| TLLA otras_instrucciones TLLC	{ yyerrok; }							

/* -------------------------------------------------------------------------- */
let_statement : TLET TID TIGUAL expresion TPCOMA
			  | TLET declaracion_arreglo TIGUAL expresion TPCOMA
              | TLET error { yyerror("Error en la instruccion LET"); } 

/* -------------------------------------------------------------------------- */
if_statement : inst_relacionada { yyerrok; }
			 | inst_abierta { yyerrok; }

/* -------------------------------------------------------------------------- */
while_statemet : TWHILE TPARA expresion TPARC TLLA otras_instrucciones TLLC
			   | TWHILE error { yyerror("Error en la instruccion WHILE"); } 

/* -------------------------------------------------------------------------- */
do_statement : TDO llamado_subrutina TPCOMA 			 

/* -------------------------------------------------------------------------- */
return_statement : TRETURN expresion TPCOMA
				 | TRETURN TPCOMA
				 | TRETURN error { yyerror("Error en la instruccion RETURN"); } 

/* -------------------------------------------------------------------------- */
inst_relacionada : TIF TPARA expresion TPARC inst_relacionada TELSE inst_relacionada
				 | otras_instrucciones

/* -------------------------------------------------------------------------- */
inst_abierta : TIF TPARA expresion TPARC if_statement
			 | TIF TPARA expresion TPARC inst_relacionada TELSE inst_abierta
             | TIF error if_statement { yyerror("Error en la instruccion IF"); } 
             | TIF error inst_relacionada TELSE inst_abierta { yyerror("Error en la instruccion IF"); }

/* -------------------------------------------------------------------------- */
expresion : expresion TAND expresion  
		   | expresion TOR expresion  
	       | expresion TMAS expresion 
		   | expresion TMENOS expresion 
           | expresion TDIV expresion
		   | expresion TMULT expresion  
		   | expresion TIGUAL expresion  	    
		   | expresion TMAY expresion 
		   | expresion TMEN expresion 
		   | TPARA expresion TPARC 
		   | llamado_subrutina 
     	   | declaracion_arreglo
		   | TMENOS expresion %prec TMENOSU 
		   | TNOT expresion 
		   | constante 
		   | TID	
		   | TTHIS

/* -------------------------------------------------------------------------- */
constante : TCTECHAR
		  | TCTEINT

/* -------------------------------------------------------------------------- */
llamado_subrutina : llamado_metodo 	
				  | llamado_funcion 

/* -------------------------------------------------------------------------- */
llamado_metodo : TID TPUNTO TID TPARA TPARC 
			   | TID TPUNTO TID TPARA parametros_actuales TPARC 

/* -------------------------------------------------------------------------- */
llamado_funcion : TID TPARA TPARC 
				| TID TPARA parametros_actuales TPARC 

/* -------------------------------------------------------------------------- */
parametros_actuales : expresion 
					| parametros_actuales TCOMA expresion 
					| parametros_actuales error { yyerror("Error en los parametros actuales de la funcion"); }


/* -------------------------------------------------------------------------- */
declaracion_arreglo : TID TCRA expresion TCRC 
					| TID TCRA error { yyerror("Error en los parametros actuales de la funcion"); }

/* -------------------------------------------------------------------------- */
%%



int yyerror(char *s)
{
	// printf(" Error de Sintaxis en la lÌnea %d cerca al simbolo %s, %s \n",nrolinea, yytext, s); 
	if (strcmp(s,"syntax error") != 0)
		printf(" Error de Sintaxis en la linea %d: %s \n", nrolinea, s);
	else
		printf(" Error de Sintaxis en la linea %d, cerca al simbolo %s \n", nrolinea, yytext);
	return 0;
}

/* Añade un simbolo en la tabla de simbolos */
void install_symbol(char *name)
{
    put_symbol(name);
}

/* Determina si existe un simbolo en la tabla */ 
int is_symbol(char *name)
{
    struct registro *simbolo;
    simbolo = get_symbol(name);
    if (simbolo == NULL)
    	return 0;
    return 1;
}

/* Agrega un simbolo de la tabla a la lista de variables*/
struct lista_variables *add_var(struct lista_variables *vars, struct registro *rc)
{
    struct lista_variables *ptr;
    ptr = (struct lista_variables *)malloc(sizeof(struct lista_variables));
    ptr->variables = rc;
    ptr->sgte = (struct lista_variables *)vars;
    vars = ptr;
    return ptr;
}

struct lista_parametros *add_par(struct lista_parametros *parm, struct registro *rc)
{
    struct lista_parametros *ptr;
    ptr = (struct lista_parametros *)malloc(sizeof(struct lista_parametros));
    ptr->parametros = rc;
    ptr->sgte = (struct lista_parametros *)parm;
    parm = ptr;
    return ptr;
}


int in_lista_parm(struct lista_parametros *lstpar, struct registro *reg)
{
    struct lista_parametros *tmp;
    for(tmp = lstpar; tmp != NULL; tmp = tmp->sgte)
    {
        if(strcmp(tmp->parametros->id,reg->id) == 0)
            return 1;
    }
    return 0;
}


int in_lista_var(struct lista_variables *lstvars, struct registro *reg)
{
	struct lista_variables *tmp;
	for(tmp = lstvars; tmp != NULL; tmp = tmp->sgte)
	{
		if(strcmp(tmp->variables->id,reg->id) == 0 && tmp->variables->atributos->ID_funcion == reg->atributos->ID_funcion)
			return 1;
	}
	return 0;
}

int num_parm(struct lista_parametros *lstpar)
{
	int parm = 0;
	struct lista_parametros *tmp;
	if(tmp == NULL)
		return parm;
	else
	{
		for(tmp = lstpar; tmp != NULL; tmp = tmp->sgte)
			parm++;
	}
	return parm;
}

/* Imprime cuando encuentra un error semantico */
void errorSemantico(char *s)
{
	printf("Error Semantico en la linea %d:", nrolinea);
    printf(" %s\n",s);
}


